#include "sled/sled_class/sled_class.h"
#include "asm/mcpwm.h"
#include "gpio.h"
#define LOG_TAG "[SLED BOARD]"
struct private_param
{
    uint32_t port_io;
    enum gpio_port port;
    uint32_t pin;
    int pwm_id;
    uint16_t count;
}sled_board_private_param;

#define PRIVATE_PARAM() ((struct private_param*)(class->private_param))

inline static uint32_t port_io_iobit(uint32_t port_io)
{
    uint32_t ret;
    ret = 0x01 << (port_io & 0x000f);
    return ret;
}

inline static uint32_t port_io_port(uint32_t port_io)
{
    return port_io >> 4;
}

inline static sled_disable_af_pwm(sled_class_t *class)
{
    gpio_disable_function(port_io_port(class->port_io),port_io_iobit(class->port_io),class->pwm_func_enum);
}

inline static sled_enable_af_pwm(sled_class_t *class)
{
    gpio_set_function(port_io_port(class->port_io),port_io_iobit(class->port_io),class->pwm_func_enum);
}

static void sled_init(sled_class_t *class)
{
    uint32_t port;
    class->spin_lock();
    struct mcpwm_config pwm = 
    {
        .aligned_mode = MCPWM_EDGE_ALIGNED,
        .ch = MCPWM_CH0,
        .complementary_en = 0,
        .h_pin = class->port_io,
        .l_pin = -1,
        .frequency = 3500,
        .duty = class->pwm_duty,
        .detect_port = -1,
        .edge = MCPWM_EDGE_FAILL,
        .irq_cb = NULL,
        .irq_priority = 1,
    }; 
    struct gpio_config led_io_init = 
    {
        .mode = PORT_INPUT_FLOATING,
    };
    class->pwm_handle = mcpwm_init(&pwm);
    mcpwm_start(class->pwm_handle);
    class->pwm_func_enum = PORT_FUNC_MCPWM0_H;
    sled_disable_af_pwm(class);
    port = port_io_port(class->port_io);
    led_io_init.pin = port_io_iobit(class->port_io);
    gpio_deinit(port,led_io_init.pin);
    gpio_init(port, &led_io_init);
    class->io_dir = IO_DIR_IN;
    class->WorkMode = SLED_MODE_SWITCH;
    class->spin_unlock();
}

#if 0
static int sled_get_pwm_handler(sled_class_t *class)
{
    struct mcpwm_config pwm = 
    {
        .aligned_mode = MCPWM_EDGE_ALIGNED,
        .ch = MCPWM_CH0,
        .complementary_en = 0,
        .h_pin = class->port_io,
        .l_pin = -1,
        .frequency = 1000,
        .duty = class->pwm_duty,
        .detect_port = -1,
        .edge = MCPWM_EDGE_FAILL,
        .irq_cb = NULL,
        .irq_priority = 1,
    }; 
    if(class->WorkMode == (SLED_MODE_PWM || SLED_MODE_BREATHE)) return class->pwm_handle;
    switch (class->WorkMode)
    {
    case SLED_MODE_SWITCH:
        break;
    
    default:
        break;
    }
    logln_pos();
    class->pwm_handle = mcpwm_init(&pwm);
    logln_pos();
    log_info("PWM_HANDLE:%d\n", class->pwm_handle);
    mcpwm_start(class->pwm_handle);;
    return class->pwm_handle;
}
#endif
static void sled_set_mode_switch(sled_class_t *class)
{
    uint32_t port;
    struct gpio_config led_io_init = 
    {
        .mode = PORT_INPUT_FLOATING,
    };
    if(class->WorkMode == SLED_MODE_SWITCH)
        return;
    switch (class->WorkMode)
    {
        case SLED_MODE_PWM:
        case SLED_MODE_BREATHE:
            sled_disable_af_pwm(class);
        break;
        default:
        break;
    }
    gpio_set_mode(port_io_port(class->port_io),port_io_iobit(class->port_io),PORT_INPUT_FLOATING);
    class->io_dir = IO_DIR_IN;
}

inline static void sled_clear_breathe_timer(sled_class_t *class)
{
    if(class->breathe_timer_handle != 0)
    {
        usr_timer_del(class->breathe_timer_handle);
        class->breathe_timer_handle = 0;
    }
}

inline static sled_enable_pwm_output(sled_class_t *class)
{
    gpio_set_mode(port_io_port(class->port_io),port_io_iobit(class->port_io),PORT_OUTPUT_LOW);
    sled_enable_af_pwm(class);
}

static void sled_set_mode(sled_class_t *class, slde_mode_t sled_mode)
{
    if(class->WorkMode == sled_mode)
    {
        return;
    }
    class->spin_lock();
    switch (sled_mode)
    {
    case SLED_MODE_SWITCH:
            sled_clear_breathe_timer(class);
            sled_set_mode_switch(class);
        break;
    case SLED_MODE_PWM:
            sled_clear_breathe_timer(class);
            sled_enable_pwm_output(class);
        break;
    case SLED_MODE_BREATHE:
            sled_clear_breathe_timer(class);
            sled_enable_pwm_output(class);
        break;
    default:
        break;
    }
    class->spin_unlock();
    class->WorkMode = sled_mode;
}

#define SPWM_TABLE_SIZE 300
#define SPWM_TABLE_TIME_GAP_MS 10
const uint16_t spwm_6s_half[]= {1,2,3,5,8,11,15,19,24,30,36,42,49,57,65,74,83,93,103,114,125,137,150,163,176,190,205,220,235,252,268,285,303,321,340,359,379,399,420,441,463,485,508,531,555,579,604,629,655,681,708,735,762,790,819,848,877,907,937,968,999,1031,1063,1095,1128,1162,1195,1229,1264,1299,1334,1370,1406,1443,1480,1517,1555,1593,1632,1671,1710,1750,1790,1830,1871,1912,1953,1995,2037,2079,2122,2165,2208,2251,2295,2339,2384,2429,2474,2519,2565,2611,2657,2703,2750,2797,2844,2891,2939,2986,3034,3083,3131,3180,3229,3278,3327,3376,3426,3476,3526,3576,3626,3677,3727,3778,3829,3880,3931,3982,4033,4085,4136,4188,4240,4291,4343,4395,4447,4499,4551,4604,4656,4708,4760,4813,4865,4917,4970,5022,5074,5127,5179,5231,5284,5336,5388,5440,5492,5545,5597,5648,5700,5752,5804,5856,5907,5959,6010,6061,6112,6163,6214,6265,6315,6366,6416,6466,6516,6566,6616,6665,6714,6763,6812,6861,6910,6958,7006,7054,7101,7149,7196,7243,7290,7336,7382,7428,7474,7519,7564,7609,7653,7698,7742,7785,7829,7872,7914,7957,7999,8040,8082,8123,8164,8204,8244,8284,8323,8362,8401,8439,8477,8514,8551,8588,8624,8660,8695,8730,8765,8799,8833,8867,8900,8932,8964,8996,9027,9058,9088,9118,9148,9177,9205,9233,9261,9288,9315,9341,9367,9392,9417,9441,9465,9488,9511,9533,9555,9577,9597,9618,9638,9657,9676,9694,9712,9729,9746,9762,9778,9793,9808,9822,9835,9848,9861,9873,9884,9895,9906,9916,9925,9934,9942,9950,9957,9963,9969,9975,9980,9984,9988,9991,9994,9996,9998,9999,9999,9999};

static void sled_breathe_handler(sled_class_t *class)
{
    int16_t table_index;
    if(class->breathe_dir == 0)
    {
        table_index = class->breathe_index - class->breathe_speed;
        if(table_index < 0)
        {
            class->breathe_dir = 1;
            table_index = 0;
        }
    }
    else
    {
        table_index = class->breathe_index + class->breathe_speed;
        if(table_index > SPWM_TABLE_SIZE - 1)
        {
            class->breathe_dir = 0;
            table_index = SPWM_TABLE_SIZE - 1;
        }
    }
    class->breathe_index = table_index;
    mcpwm_set_duty(class->pwm_handle, spwm_6s_half[table_index]);
}

static void sled_on_breathe(sled_class_t *class)
{
    if(class->WorkMode != SLED_MODE_BREATHE)
    {
        class->SetMode(SLED_MODE_BREATHE);
        return;
    }
    if(class->breathe_timer_handle != 0)
    {
        usr_timer_del(class->breathe_timer_handle);
        class->breathe_timer_handle = 0;
    }
    class->breathe_timer_handle = usr_timer_add(class,sled_breathe_handler,10,0);
}

static void sled_on1(sled_class_t *class)
{
    class->spin_lock();
    switch (class->WorkMode)
    {
    case SLED_MODE_SWITCH:
        if(class->io_dir != IO_DIR_OUT) gpio_set_mode(port_io_port(class->port_io),port_io_iobit(class->port_io),PORT_OUTPUT_LOW),class->io_dir = IO_DIR_OUT;
        gpio_write(class->port_io,1);
        break;
    case SLED_MODE_PWM:
        // mcpwm_
    default:
        break;
    }
    class->spin_unlock();
}

static void sled_on0(sled_class_t *class)
{
    class->spin_lock();
    switch (class->WorkMode)
    {
    case SLED_MODE_SWITCH:
        if(class->io_dir != IO_DIR_OUT) gpio_set_mode(port_io_port(class->port_io),port_io_iobit(class->port_io),PORT_OUTPUT_LOW),class->io_dir = IO_DIR_OUT;
        gpio_write(class->port_io,0);
        break;
    case SLED_MODE_PWM:
        // mcpwm_
    default:
        break;
    }
    class->spin_unlock();
}


static void sled_off(sled_class_t *class)
{
    class->init();
    class->WorkMode = SLED_MODE_SWITCH;
}
SLED_CONTRUCTOR(sled_board,IO_PORTB_02);
